%{
#include <stdio.h>
#include <vector>
#include <map>
#include "liza.h"
#include "builder.h"

int yyerror(char *msg);
void prepareModule(Module *module);
Module *generateCode();
int yylex();

vector<SymbolNode> symbolTable;
vector<SymbolNode> gsymbolTable;
vector<TreeNode> defines;
map<string, Value*> variables;

//vector<TreeNode> nodes;
class SymbolNode;
%}

%union {
  int ival;
  char *sval;
  class SymbolNode *symbolNode;
  class TreeNode *treeNode;
  class LoopNode *loopNode;
}


%{

Value *getLocalVariable(string name);
void addVariable(SymbolNode symbolNode);
void addDefine(TreeNode *defineNode);
Value* addAssign(TreeNode *identNode, Value *val);
void addGAssign(TreeNode *identNode, Value *val);
void handleInvokation(TreeNode node);
void invokeMethod(string name, TreeNode params);
void createDefine(TreeNode *node,Module *module);
void initMainFunction(TreeNode *node);
void initCallDef(TreeNode *node);

bool isFor = false;

bool rejected = false;

TreeNode *tNode = new TreeNode(nPROGRAM);

BasicBlock *cBlock = NULL;

BasicBlock *cRetBlock = NULL;

Function *cFunction = NULL;

Module *cMod = NULL;

Function *mainFunction = NULL;

BasicBlock *mainfBlock = NULL;

BasicBlock *mainfRetBlock = NULL;


%}

%token <sval>kID kSTRCONST
%token <ival>kINT

%token tASSIGN
       tDEF
       tEND
       tLOOP
       tTIMES
       tRETURN
       tLBRACKET
       tRBRACKET
       tCOMMA
       tGLOBALPREFIX
       tADD
       tDIV
       tSUBS
       tMULT

%type <symbolNode>string
%type <symbolNode>integer
%type <symbolNode>identifier

%type <treeNode>string_expression
%type <treeNode>integer_expression

%type <treeNode>assign_expression
%type <treeNode>def_invoke
%type <treeNode>invoke_params
%type <treeNode>invoke_param
%type <treeNode>assign

%type <treeNode>statement

%type <treeNode>integer_simple

%type <treeNode>block_statement
%type <treeNode>block
%type <treeNode>def_define
%type <treeNode>def_statement
%type <treeNode>def_param
%type <treeNode>def_params
%type <treeNode>program
%type <loopNode>loop_statement
%type <loopNode>loop_st_def

%start program

%%
 // program ----
  program : { 
               $$=tNode; 
	       initMainFunction(tNode);
            }
            | program statement { $$ = $1; 
                                  $$->child.push_back(*$2);}
  ;
//block  ---
  block : {  $$ = new TreeNode(nBLOCK);}
        | block block_statement
  ;
//statement ---
  statement : block_statement { $$ = $1; }
            | def_statement { $$ = $1; }
  ;  
//block statement ---
  block_statement : assign         { $$ = $1;}
                  | def_invoke     { $$ = $1; }
                  | loop_statement { $$ = $1;} 
  ;
//subroutines [begin]                  
  def_define : tDEF identifier tLBRACKET def_params tRBRACKET 
                                     {
                                         $$ = new TreeNode(nDEF_DEFINE);
                                         $$->symbolNode = $2;
					 $$->child.push_back(*$4);
					 addDefine($$);
                                     }
  ;
  
  def_statement : def_define block tEND
                                     {
                                         $$ = new TreeNode(nDEF);
					 $$->child.push_back(*$1);
					 $$->child.push_back(*$2);
                                     }
  ;

  def_param : /*empty*/  { $$ = new TreeNode(nBLANK);  }
            | identifier { $$ = new TreeNode(nDEF_PARAM);
                           $$->symbolNode = $1;}
  ;

  def_params : def_param  {  $$ = new TreeNode(nDEF_PARAMS);
                             $$->child.push_back(*$1);
                          }
             | def_params tCOMMA def_param 
                          {
			    $$ = $1;
			    $$->child.push_back(*$3);
	                  }      
  ;
//subroutines [end]

//subroutines invocation [begin]

  def_invoke : identifier tLBRACKET invoke_params tRBRACKET   
                                     {   $$ = new TreeNode(nINVOKE);
                                         TreeNode idNode = TreeNode(nIDENT);
                                         idNode.symbolNode = $1;
					 $$->child.push_back(idNode);
                                         $$->child.push_back(*$3);
					 initCallDef($$);
                                     }
  ;
  invoke_params : invoke_param       {
                                       $$ = new TreeNode(nPARAMS);
                                       $$->child.push_back(*$1);
				     } 
                | invoke_params tCOMMA invoke_param    
                                     { $$ = $1;
                                       if($$->child[0].nodeType!=nBLANK){
                                            $$->child.push_back(*$3);
                                       }else{
                                            yyerror("Parameter can't be blank\n");
                                       }
                                     }
  ;

  invoke_param : /*empty*/           { $$ = new TreeNode(nBLANK); } 
               |  identifier         { $$ = new TreeNode(nPARAM);
                                       TreeNode identNode = TreeNode(nIDENT);
				       identNode.symbolNode = $1;
				       $$->child.push_back(identNode);
                                     }
               |  assign_expression  { $$ = new TreeNode(nPARAM);
                                       $$->child.push_back(*$1);  
				       $$->value = createStringValue($1->value, cBlock);
		                     }
  ;

//subroutines invocation [end]

//loop [begin]
  loop_st_def : tLOOP integer_expression tTIMES  
                                      {
					  $$ = buildForHeadBlock(cBlock, $2->value);
					  $$->child.push_back(*$2);					  
					  $$->value = $$->entryBlock;
					  cBlock = cast<BasicBlock>($$->value);
                                      }   
  ;
  
  loop_statement : loop_st_def block tEND
                                      {
					$$ = $1;
					$$->child.push_back(*$2);
					buildIncrInstr($1, cBlock);
					BasicBlock *bb = getNextBlock($1->condBlock);
					if(bb!=0){
					  new BranchInst($1->entryBlock, bb, $1->cond, $1->condBlock);
					}
					if(cBlock->getTerminator()==0){
					    new BranchInst($1->condBlock, cBlock); 
					}  
					cBlock = $1->nextBlock;
				      }
  ;
//loop [end]

  assign : identifier tASSIGN assign_expression 
                                         {  
					    TreeNode identNode = TreeNode(nIDENT);                                         
                                            DataType dataType;
                                            if($3->nodeType == nSTRING_EXPRESSION){
                                               dataType = tSTRING;					       
                                            } 
                                            if($3->nodeType == nINTEGER_EXPRESSION){
                                               dataType = tINTEGER;
                                            }
                                            identNode.symbolNode = $1;
					    identNode.symbolNode->dataType = dataType;
                                            $$ = new TreeNode( nASSIGN );
					    $$->child.push_back(identNode);
					    $$->child.push_back(*$3); 
					    if($1->symbolType==sID)
					      addAssign($$, $3->value); 
					    else
					      addGAssign($$, $3->value);
					  };           

  assign_expression : string_expression       {  $$ = $1; }
                    | integer_expression      {  $$ = $1; }
  ;

  string : kSTRCONST    {  $$ = new SymbolNode ( sCONSTANT , tSTRING ); 
                           $$->s_value = $1;
                        };

  integer : kINT        { $$ = new SymbolNode ( sCONSTANT, tINTEGER );
                          $$->i_value = $1;
                        };

  identifier : kID      {  $$ = new SymbolNode ( sID );
                           $$->name = $1;
                        }
             | tGLOBALPREFIX kID      
                        {
			  $$ = new SymbolNode ( sGID );
			  $$->name = $2;
                        };
  
//integer expression [begin]

  integer_simple : integer { 
                             $$ = new TreeNode(nINTEGER_VALUE);
		   	     $$->symbolNode = $1;
			     $$->value=storeInteger($1->i_value, cMod);			     
                           }                
  ;

  integer_expression : integer_simple
                                        { 
					  $$ = new TreeNode(nINTEGER_EXPRESSION);
					  TreeNode intConstant = TreeNode(nINTEGER_CONSTANT);
					  intConstant.child.push_back(*$1);
					  $$->child.push_back(intConstant);
					  $$->value = $1->value;
                                        }
                     | integer_expression tADD integer_simple
                                        {
					  $$ = new TreeNode(nINTEGER_EXPRESSION);
					  TreeNode addNode = TreeNode(nADD);
					  addNode.child.push_back(*$1);
					  addNode.child.push_back(*$3);
					  $$->child.push_back(addNode);
					  $$->value=buildAddOperation($1->value, $3->value, cBlock);
					}
                     | integer_expression tSUBS integer_simple 
                                        {
 					  $$ = new TreeNode(nINTEGER_EXPRESSION);
					  TreeNode addNode = TreeNode(nADD);
					  addNode.child.push_back(*$1);
					  addNode.child.push_back(*$3);
					  $$->child.push_back(addNode);
					  $$->value=buildSubOperation($1->value, $3->value, cBlock);
					}
                     | integer_expression tMULT integer_simple 
                                        {
 					  $$ = new TreeNode(nINTEGER_EXPRESSION);
					  TreeNode addNode = TreeNode(nADD);
					  addNode.child.push_back(*$1);
					  addNode.child.push_back(*$3);
					  $$->child.push_back(addNode);
					  $$->value=buildMulOperation($1->value, $3->value, cBlock);
					}
                     | integer_expression tDIV integer_simple 
                                        {
 					  $$ = new TreeNode(nINTEGER_EXPRESSION);
					  TreeNode addNode = TreeNode(nADD);
					  addNode.child.push_back(*$1);
					  addNode.child.push_back(*$3);
					  $$->child.push_back(addNode);
					  $$->value=buildDivOperation($1->value, $3->value, cBlock);
					}

  ;
//integer expression [end]  
   string_expression : identifier           
                                         {  
					    /* std::cout<<"getting local variable "<<$1->name<<endl; */
                                            $$ = new TreeNode(nSTRING_EXPRESSION);
					    TreeNode valNode = TreeNode(nIDENT); 
					    valNode.symbolNode = $1;
					    $$->child.push_back(valNode); 
					    $$->value = getLocalVariable($1->name);
                                         } |  
                     string              { 
		                            $$ = new TreeNode(nSTRING_EXPRESSION);
		                            TreeNode valNode = TreeNode(nSTRING_VALUE);
		                            valNode.symbolNode = $1;		   
					    TreeNode strConstant = TreeNode(nSTRING_CONSTANT);
					    $$->value = createStringConstant(cMod, $1->s_value);
					    strConstant.child.push_back(valNode);					    
					    $$->child.push_back(strConstant);					    
                                          } |
		     identifier tADD string_expression
                                          {
					    $$ = new TreeNode(nSTRING_EXPRESSION);
					    TreeNode plusNode = TreeNode(nSTRING_PLUS);
					    TreeNode stringNode = TreeNode(nIDENT);
					    stringNode.symbolNode = $1;
					    stringNode.value = getLocalVariable($1->name);					    
					    plusNode.child.push_back(stringNode);
					    plusNode.child.push_back(*$3);
					    $$->child.push_back(plusNode);
					    $$->value=createStringConcat(stringNode.value, $3->value, cBlock, cMod);
					  }|
 		     string tADD string_expression
                                          {
					    $$ = new TreeNode(nSTRING_EXPRESSION);
					    TreeNode plusNode = TreeNode(nSTRING_PLUS);
					    TreeNode stringNode = TreeNode(nIDENT);
					    stringNode.symbolNode = $1;
					    stringNode.value = createStringConstant(cMod, $1->s_value);
					    plusNode.child.push_back(stringNode);
					    plusNode.child.push_back(*$3);
					    $$->child.push_back(plusNode);
					    $$->value=createStringConcat(stringNode.value, $3->value, cBlock, cMod);
					  }

                    /* | string_expression tADD string   */
                    /*                       { $$ = new TreeNode(nSTRING_EXPRESSION); */
                    /*                	    TreeNode plusNode = TreeNode(nSTRING_PLUS); */
                    /*               	    TreeNode strNode = TreeNode(nSTRING_VALUE); */
                    /*      		    strNode.symbolNode = $3; */
		    /*                         plusNode.child.push_back(*$1); */
		    /*                         plusNode.child.push_back(strNode); */
		    /* 			    $$->child.push_back(plusNode); */
                    /*                       } */

                    /* | string_expression tADD identifier */
                    /*                       { $$ = new TreeNode(nSTRING_EXPRESSION); */
                    /*                	    TreeNode plusNode = TreeNode(nSTRING_PLUS); */
                    /*               	    TreeNode idNode = TreeNode(nIDENT); */
                    /*      		    idNode.symbolNode = $3; */
		    /*                         plusNode.child.push_back(*$1); */
		    /*                         plusNode.child.push_back(idNode); */
		    /* 			    $$->child.push_back(plusNode); */
                    /*                       } */
                    /* | string_expression tADD def_invoke */
                    /*                       { $$ = new TreeNode(nSTRING_EXPRESSION); */
                    /*                	    TreeNode plusNode = TreeNode(nSTRING_PLUS); */
		    /*                         plusNode.child.push_back(*$1); */
		    /*                         plusNode.child.push_back(*$3); */
		    /* 			    $$->child.push_back(plusNode); */
                    /*                       } */
 ;		    
                
%%

int yyerror(char *msg){
  printf("[Error]:%s\n", msg);
  exit(0);
  return 0;
}

void initMainFunction(TreeNode *node){
  Function *func = buildMainFunction(node->module);
  mainFunction = func;
  cFunction = mainFunction;

  BasicBlock *retBlock = new BasicBlock("retBlock", func);
  mainfRetBlock = retBlock;
  cRetBlock = mainfRetBlock;

  new ReturnInst (ConstantInt::get(Type::Int32Ty, 0),retBlock);

  BasicBlock *mainBlock = new BasicBlock("mainBlock", func);  
  mainBlock->moveBefore(retBlock);
  mainfBlock = mainBlock;


  node->block = mainBlock;
  cBlock = mainBlock;
  cMod = node->module;  
}

void addDefine(TreeNode *defineNode){
  SymbolNode defNode = *defineNode->symbolNode;
  bool found = false;
  for(int i=0;i<defines.size();i++){
    if(defNode.name==defines[i].symbolNode->name
       && defineNode->child[0].child.size() == defines[i].child[0].child.size()){
      found = true;
    }    
  }
  if(!found){
    defines.push_back(*defineNode);
  }else{
    char errmsg[256]; 
    sprintf(errmsg,"There is def `%s` defined later with same arguments number", defNode.name.c_str()); 
    yyerror(errmsg); 
  }
}

void addGAssign(TreeNode *assignNode, Value* expression){
  SymbolNode symbolNode = *assignNode->child[0].symbolNode;
  bool found = false;
  for(int i=0;i<gsymbolTable.size();i++){
    SymbolNode symbol = gsymbolTable[i];    
    if(symbol.name == symbolNode.name){ 
       found = true; 
       if(symbol.dataType != symbolNode.dataType){ 
     	  char errmsg[256]; 
     	  sprintf(errmsg,"There is global variable `%s` defined later with other type", symbol.name.c_str()); 
     	  yyerror(errmsg); 
       } 
    } 
  }
  if(!found){
    gsymbolTable.push_back(*assignNode->child[0].symbolNode);
    SymbolNode symbol = *assignNode->child[0].symbolNode;
    TreeNode *assign_expr = &assignNode->child[1];
    if(assign_expr->child[0].nodeType != nINTEGER_CONSTANT 
        && assign_expr->child[0].nodeType != nSTRING_CONSTANT){ 
       char errmsg[256];  
       sprintf(errmsg,"Value of global variable %s must be a constant", symbol.name.c_str());       
       yyerror(errmsg);
       return;
    } 

    /* if(symbol.dataType == tINTEGER){  */
    /*   int value = assign_expr->child[0].child[0].symbolNode->i_value; */
    /*   assignNode->value=buildGlobalIntInit(cMod, symbol.name, value);  */
    /* }  */
    
    /* if(symbol.dataType == tSTRING){ */
    /*   string value = assign_expr->child[0].child[0].symbolNode->s_value; */
    /*   assignNode->value=buildGlobalStrInit(cMod, symbol.name, value); */
    /* } */

  }
}

Value *getLocalVariable(string name){
  return variables[name];
}

Value* addAssign(TreeNode *assignNode, Value* expression){
  SymbolNode symbolNode = *assignNode->child[0].symbolNode;
  bool found = false;
  for(int i=0;i<symbolTable.size();i++){
    SymbolNode symbol = symbolTable[i];    
    if(symbol.name == symbolNode.name){ 
       found = true; 
       if(symbol.dataType != symbolNode.dataType){ 
     	 char errmsg[256]; 
     	 sprintf(errmsg,"There is variable `%s` defined later with other type", symbol.name.c_str()); 
     	 yyerror(errmsg); 
	 return NULL;
       } 
    } 
  }

  Value *value = NULL;

  SymbolNode symbol = *assignNode->child[0].symbolNode;

  if(!found){
    symbolTable.push_back(*assignNode->child[0].symbolNode);
    /* if(symbol.dataType == tINTEGER){  */
    /*   buildLocalIntInit(cBlock, symbol.name, symbol.i_value);  */
    /* } */
    if(symbol.dataType == tSTRING){
      value = initStringLocalVar(symbol.name, cBlock, expression, cMod);
      variables.insert(std::pair<string,Value*>(symbol.name,value));
    }
  }else{
    if(symbol.dataType == tSTRING){
      std::cout<<"string local assign"<<endl;
    }    
  }
  
  return value;
}

void prepareModule(Module *module){
  tNode->module = module;
  cMod = module;
  makeStandardDefines(module);
}

Module* generateCode(){
  Module* module = tNode->module;

  if(mainfBlock->getTerminator()==NULL){
    new BranchInst(mainfRetBlock, mainfBlock);
  }

  if(cBlock->getTerminator()==NULL){
    new BranchInst(mainfRetBlock, cBlock);
  }

  Function *f = cBlock->getParent();
  for(Function::iterator i = f->begin();i!=f->end();++i){
    if(i->getTerminator()==0){
       new BranchInst(cRetBlock, i);
    }
  }


  //make return at main_block
  /* new ReturnInst (ConstantInt::get(Type::Int32Ty, 0),tNode->block); */

  /* for(int i=0;i<tNode->child.size();i++){ */
  /*   TreeNode currentNode = tNode->child[i]; */
  /*   if(currentNode.nodeType == nASSIGN){   */
      
  /*   } */
  /*   if(currentNode.nodeType == nINVOKE){ */
      
  /*   } */
  /*   if(currentNode.nodeType == nDEF){ */
      
  /*   } */
  /* } */
  
  
  verifyModule(*module, PrintMessageAction);

  PassManager PM;
  PM.add(new PrintModulePass(&llvm::cout));
  PM.run(*module);
  return module;
}

int count_ = 0;
void createDefine(TreeNode *node,Module *module){
  
}

void createInvokation(TreeNode *node, Module *module){
  
}

void handleInvokation(TreeNode node){
  TreeNode idNode = node.child[0];
  string name = idNode.symbolNode->name;
  invokeMethod(name, node.child[1]);
}

void invokeMethod(string name, TreeNode params){

}

void initCallDef(TreeNode *node){
  string name = node->child[0].symbolNode->name;
  vector<Value*> params;
  for(int i=0;i<node->child[1].child.size();i++){
    params.push_back(node->child[1].child[i].value);
  }
  Function *func;
  if((func = cMod->getFunction(name))==NULL){
    //TODO: error message or make function definition    
  }else{
    callDef(func,params, cBlock);
  }
}
